// ==UserScript==
// @name         莉泽のB站屏蔽
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  过滤广告和特定类型的视频
// @author       sunyeojin
// @icon            https://www.bilibili.com/favicon.ico
// @icon            https://www.bilibili.com/history
// @match           https://www.bilibili.com/*
// @match           https://live.bilibili.com/*
// @match           https://search.bilibili.com/*
// @match           https://space.bilibili.com/*
// @match           https://account.bilibili.com/*
// @match           https://message.bilibili.com/*
// @match           https://t.bilibili.com/*
// @match           https://link.bilibili.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 常量数组：包含需要匹配的关键词，可以是字符串或正则表达式对象
    // 示例：字符串会进行包含匹配，正则表达式使用 /.../ 或 new RegExp() 创建
    const KEYWORDS = [
        //傻逼内容
        /(偷拍|相亲|彩礼|结婚|离婚)/i,
        /(女权|女拳|小仙女|女神|巴西牛排)/i,
        /(男|南|楠|伪|药)(娘|梁|酿)/i,
        /(变|成|为|转|生|当|做|以|用|扮|穿).*女/i,
        /(变性|性转|雌堕|扶她|扶他|两套|两性|双性|阴阳人)/i,
        /(牛牛|牛子).+(不见|消失|没|无)/i,
        //自以为是
        /(好心|苦心|耐心)(解释|说明|提示|提醒)/i,
        /(不明白|不知道|不清楚|不理解).+(为什么|为何|为啥|怎么)/i,
        /(居然|竟然)不(了解|知道|清楚|会|懂)/i,
        /(如何|怎样|怎么)(快速|迅速|高效)/i,
        /(教养|情商|一定|启迪人生|建议收藏)/i,
        //引战话题
        /本以为.+结果/i,
        /(为什么|为何|怎么|怎)(回来|归来)(时|后)/i,
        /(请问|如果|若|假如)(你|我)(可以|能)/i,
        /(哪个|谁).+(强|厉害|牛)/i,
        /(请问|如果|若|假如).+会(怎么样|怎样|如何|发生什么)/i,
        /看起来(很|比较|非常|特别)/i,
        /后.+(该|如何|怎么|怎样)/i,
        /(为什么|为何).+(不|反而|但是|却|其他|其它|别的)/i,
        /中国.+(为什么|为何)/i,
        //小鬼乱刷
        /(黑暗森林法则|智子|罗辑|刘慈欣|三体文明|三体小说)/i,
        /(奥特曼|舞法天女|巴啦啦小魔仙)/i,
        //垃圾广告
        /(广告|推广|痤疮|痔疮|减肥|肾虚)/i,
        /(找|拆).+字/i,
        //汉奸败类
        /(摔倒|倒地|碰瓷)/i,
        /(公知|易中天)/i,
        /(谣言|谣传|误导)/i,
        //弱智作者
        /智商(超过|超越)/i,
        /垃圾(技能|能力|天赋)/i,
        /(弱智|低智商|玄学)/i,
        /(不以为然|不以为意|太惨了)/i,
        /.+(学不存在了)/i,
    ];
    // 目标父级类名
    const CARD_CLASSES = ['feed-card','history-card','bili-video-card','video-page-card-small'];
    // 广告ID列表
    const AD_IDS = [
        //广告
        'slide_ad',
    ];
    // 广告Class列表
    const AD_CLASSES = [
        //广告
        'ad-report',
        'video-card-ad-small',
        'act-now',
        //推荐
        'recommended-swipe',
        'left-loc-entry',
    ];

    /**
     * 检查给定的文本是否匹配任何一个关键词
     * @param {string} text - 要检查的纯文本
     * @returns {boolean} - 是否匹配
     */
    function isTextMatched(text) {
        if (!text || typeof text !== 'string') return false;

        for (const keyword of KEYWORDS) {
            if (keyword instanceof RegExp) {
                if (keyword.test(text)) {
                    return true;
                }
            } else {
                // 普通字符串，进行包含匹配
                if (text.includes(keyword)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取一个元素内所有可读文本的拼接（递归遍历子节点）
     * @param {Element} element - 目标元素
     * @returns {string} - 合并后的纯文本
     */
    function getElementTextContent(element) {
        // 使用内置的 textContent 属性即可获取所有后代文本节点的内容
        // 它会自动忽略标签，只返回纯文本，并且包括所有层级的文本
        return element.textContent.trim();
    }

    /**
     * 移除页面上所有 ID 在指定数组中的元素
     * @param {string[]} idArray
     */
    function removeAdsById(idArray) {
        idArray.forEach(id => {
            const element = document.getElementById(id);
            if (element) {
                element.style = 'display:none';
            }
        });
    }

    /**
     * 移除页面上所有 class 在指定数组中的元素
     * @param {string[]} classArray
     */
    function removeAdsByClass(classArray) {
        classArray.forEach(clazz => {
            // 获取所有具有该class的元素集合
            const elementCollection = document.getElementsByClassName(clazz);
            for (let i = 0; i < elementCollection.length; i++) {
                let element = elementCollection[i];
                if (element) {
                    element.style = 'display:none';
                }
            }
        });
    }

    /**
     * 扫描所有目标卡片，检查内容并删除匹配的卡片
     */
    function scanAndRemoveElement() {
        const selector = CARD_CLASSES.map(cls => `.${cls}`).join(', ');
        const targetElements = document.querySelectorAll(selector);
        let removedCount = 0;
        targetElements.forEach((cardElement, index) => {
            if (!document.body.contains(cardElement)) {
                return;
            }
            const fullText = getElementTextContent(cardElement);
            if (isTextMatched(fullText)) {
                cardElement.remove();
                removedCount++;
            }
        });
        //广告屏蔽
        removeAdsById(AD_IDS);
        removeAdsByClass(AD_CLASSES);
    }

    // ========== 执行逻辑 ==========
    let executeCount = 0;
    let maxTimes = 50;//执行次数
    let delayMS = 200;//毫秒
    let timer = null;
    let observer = null;
    observer = new MutationObserver(function() {
        observer.disconnect();
        scanAndRemoveElement();
        executeCount = 0;
        timer = setInterval(function() {
            scanAndRemoveElement();
            executeCount++;
            if (executeCount >= maxTimes) {
                clearInterval(timer);
                console.log('扫描停止');
            }
        }, delayMS);
    });
    observer.observe(document.documentElement, {
        childList: true, // 监听子元素增删
        subtree: true, // 监听深层后代元素
        attributes: true, // 监听元素属性变化（可选，按需关闭）
        characterData: true // 监听文本内容变化（可选，按需关闭）
    });

})();